package com.audiobo.recorder;

import java.io.File;
import java.io.PrintStream;
import java.security.AccessController;
import java.security.PrivilegedAction;
import javax.swing.JApplet;
import javax.swing.SwingUtilities;
import netscape.javascript.JSException;
import netscape.javascript.JSObject;

// Referenced classes of package com.audiobo.recorder:
//            VUInlineView, VUView, AudioPlayback, AudioCapture,
//            AudioUploader, AudioPlaybackDelegate, AudioCaptureDelegate, AudioUploaderDelegate

public class AppletController extends JApplet {

  static AppletController mainApplet = null;
  static String outputTmpFilePath = null;
  static String audioFilePath = null;
  VUView vuView;
  AudioCapture capture;
  AudioPlayback playback;
  AudioUploader uploader;
  final int IS_IDLE = 0;
  final int IS_RECORDING = 1;
  final int IS_PLAYING = 2;
  final int IS_UPLOADING = 3;
  int currentTask;
  boolean playBackRun;

  public AppletController() {
    vuView = null;
    capture = null;
    playback = null;
    uploader = null;
    currentTask = 0;
    playBackRun = false;
  }

  public void init() {
    System.out.println((new StringBuilder()).append("Initing...").append(this)
        .toString());
    if (mainApplet != null) {
      System.out.println("Reiniting");
    }
    mainApplet = this;
    try {
      SwingUtilities.invokeAndWait(new Runnable() {

        public void run() {
          myInit();
        }
      });
    } catch (Exception exception) {
      System.out.println("Creating applet GUI failed");
      exception.printStackTrace();
    }
  }

  private void myInit() {
    String s = getParameter("layout");
    if (s != null && s.equals("inline")) {
      vuView = new VUInlineView();
    } else {
      vuView = new VUView();
    }
    vuView.setRenderLevels(false);
    setContentPane(vuView);
    vuView.repaint();

    try {
      SwingUtilities.invokeLater(new Runnable() {

        public void run() {
          try {
            AppletController.outputTmpFilePath = (new File(System
                .getProperty("java.io.tmpdir"), "audiobo.raw")).getPath();
            AppletController.audioFilePath = (new File(System
                .getProperty("java.io.tmpdir"), "audiobo.aiff")).getPath();
            capture = new AudioCapture();
            playback = new AudioPlayback();
            uploader = new AudioUploader();
            installWorkerDelegates();
            vuView.setLevelSource(capture);
            if (getParameter("durationLimit") != null) {
              capture.setMaxRecordingTime(Float
                  .parseFloat(getParameter("durationLimit")));
            } else {
              capture.setMaxRecordingTime(300D);
            }
            AudioCapture.checkPermissions();
          } catch (SecurityException securityexception) {
            System.out.println((new StringBuilder())
                .append("Don't have permissions for audio hardware:")
                .append(securityexception).toString());
            js_appletError("Don't have permissions for audio hardware",
                securityexception);
            return;
          }
          js_appletReady();
          //For Test
          //startRecording();
        }
      });
    } catch (Exception exception) {
      System.out.println((new StringBuilder()).append("js_appletReady failed:")
          .append(exception).toString());
    }
  }

  private void installWorkerDelegates() {
    playback.setDelegate(new AudioPlaybackDelegate() {

      public void playbackFinished() {
        js_playbackFinished();
        currentTask = 0;
      }

      public void updatePlaybackPosition(double pos, double duration) {
        js_updateTimecode((float)pos, (float)duration);
      }
    });

    capture.setDelegate(new AudioCaptureDelegate() {
      public void recordingShouldStop() {
        stop();
      }

      public void updateRecordingTime(double d) {
        js_updateTimecode((float) d, (float) d);
      }

    });

    uploader.setDelegate(new AudioUploaderDelegate() {

      float lastProgress;

      {
        lastProgress = 0.0F;
      }

      public void uploadProgressWithValue(float f) {
        if (Math.abs(f - lastProgress) > 0.02F || f == 0.0F || f >= 1.0F) {
          js_uploadProgress(f);
          lastProgress = f;
        }
      }

      public void uploadFinished() {
        js_uploadSuccess();
        currentTask = 0;
      }

    });
  }

  public void stop() {
    if (currentTask == 1) {
      stopRecording();
    }
    if (currentTask == 2) {
      stopPlayback();
    }
    if (currentTask == 3) {
      stopUpload();
    }
  }

  public boolean clearRecording() {
    if (currentTask != 0) {
      stop();
    }
    AccessController.doPrivileged(new PrivilegedAction() {

      public Object run() {
        capture.cleanUp();
        return null;
      }

    });
    return true;
  }

  public boolean startRecording(final boolean cleanupFirst) {
    if (currentTask == 1) {
      return true;
    }
    boolean flag = ((Boolean) AccessController
        .doPrivileged(new PrivilegedAction() {

          public Object run() {
            if (cleanupFirst) {
              capture.cleanUp();
            }
            return Boolean.valueOf(capture.start());
          }
        })).booleanValue();

    if (!flag) {
      js_appletError("Failed to start recording, please check your system's audio settings");
      return false;
    } else {
      vuView.setRenderLevels(true);
      currentTask = 1;
      js_recordingStarted();
      return true;
    }
  }

  public boolean startRecording() {
    System.out.println("startRecording");
    return startRecording(true);
  }

  public boolean resumeRecording() {
    System.out.println("resumeRecording");
    return startRecording(false);
  }

  public boolean stopRecording() {
    System.out.println("stopRecording");
    if (currentTask != 1) {
      return true;
    } else {
      AccessController.doPrivileged(new PrivilegedAction() {

        public Object run() {
          capture.stop();
          return null;
        }

      });
      vuView.setRenderLevels(false);
      playback.setDuration(capture.getDuration());
      currentTask = 0;
      js_recordingStopped();
      return true;
    }
  }

  public boolean startPlayback() {
    if (currentTask != 0) {
      return true;
    }
    AccessController.doPrivileged(new PrivilegedAction() {
      public Object run() {
        playBackRun = playback.start();
        return null;
      }

    });
    if (!playBackRun) {
      System.out.println("ERROR!!!");
      return false;
    } else {
      currentTask = 2;
      js_playbackStarted();
      return true;
    }
  }

  public boolean stopPlayback() {
    if (currentTask != 2) {
      return true;
    } else {
      AccessController.doPrivileged(new PrivilegedAction() {

        public Object run() {
          playback.stop();
          return null;
        }

      });
      currentTask = 0;
      js_playbackFinished();
      return true;
    }
  }

  public boolean startUpload(String s, final String fieldName) {
    System.out.println("startUpload: " + s + fieldName);
    if (currentTask != 0) {
      return true;
    }
    final String fileToUpload = s;
    AccessController.doPrivileged(new PrivilegedAction() {

      public Object run() {
        uploader.start(fileToUpload, fieldName);
        return null;
      }

    });
    if (uploader.isUploadStarted()) {
      currentTask = 3;
      js_uploadStarted();
    } else {
      js_uploadFailed("Can't upload");
    }
    return true;
  }

  public boolean stopUpload() {
    if (currentTask != 3) {
      return true;
    } else {
      AccessController.doPrivileged(new PrivilegedAction() {

        public Object run() {
          uploader.stop();
          return null;
        }

      });
      js_uploadFailed("Manually cancelled");
      currentTask = 0;
      return true;
    }
  }

  private void callJs(String s) {
    callJs(s, new Object[0]);
  }

  private void callJs(String s, Object aobj[]) {
    try {
      Object aobj1[] = new Object[aobj.length + 2];
      aobj1[0] = getParameter("elementId");
      aobj1[1] = s;
      System
          .arraycopy(((Object) (aobj)), 0, ((Object) (aobj1)), 2, aobj.length);
      JSObject.getWindow(this).call("boo_recorder_trampoline", aobj1);
    } catch (JSException jsexception) {
      //System.out.println((new StringBuilder()).append("JavaScript exception delivering ").append(s).toString());
    }
  }

  private void js_appletReady() {
    System.out.println("appletReady");
    callJs("_appletReady");
  }

  private void js_appletError(String s, Exception exception) {
    js_appletError((new StringBuilder()).append(s).append(": ")
        .append(exception.getMessage()).toString());
  }

  private void js_appletError(String s) {
    callJs("_appletError", new Object[] { s });
  }

  private void js_playbackStarted() {
    callJs("_playbackStarted");
  }

  private void js_playbackFinished() {
    callJs("_playbackFinished");
  }

  private void js_recordingStarted() {
    callJs("_recordingStarted");
  }

  private void js_recordingStopped() {
    callJs("_recordingStopped");
  }

  private void js_updateTimecode(float f, float f1) {
    callJs("_updateTimecode",
        new Object[] { Float.valueOf(f), Float.valueOf(f1) });
  }

  private void js_uploadStarted() {
    callJs("_uploadStarted");
  }

  private void js_uploadProgress(float f) {
    callJs("_uploadProgress", new Object[] { new Float(f) });
  }

  private void js_uploadSuccess() {
    callJs("_uploadSuccess");
  }

  private void js_uploadFailed(String s) {
    callJs("_uploadFailed", new Object[] { s });
  }

}
